#include "game_server.h"
#include "proto.h"
#include "game_log.h"
#include "game_redis.h"
#include "do_sql.h"

using namespace std;

int GameServer::Dispatch(NetworkSessionBase * session)
{
	int cmd = JsonTo<int>(session->_jpacket["cmd"]);
	//先筛选大厅协议
	static std::map<int, int(GameServer::*)(NetworkSessionBase*)> _hall_route =
	{
	{ SYS_ECHO ,&GameServer::HeartBeat },
	{ HEART_BEAT ,&GameServer::HeartBeat },
	{ CLIENT_OPEN_ROOM_REQ ,&GameServer::CreateRoom },
	{ CLIENT_JOIN_ROOM_REQ ,&GameServer::JoinInRoom }
	};
	auto hall_itr = _hall_route.find(cmd);
	if (hall_itr != _hall_route.end())
	{
		auto func = hall_itr->second;
		(this->*func)(session);
		return 0;
	}
	//查看用户的UID
	int uid = Find<FD_KEY>(session->_fd);
	if (uid == -1)
	{
		return -1;
	}
	//查看用户所在的房间ID
	auto roomid = GetRoom(uid);
	if (roomid == -1)
	{
		return -1;
	}
	//获取房间实例
	auto proom = _rooms[roomid];
	if (proom == nullptr)
	{
		return -1;
	}
	//进入游戏内部协议筛选
	return proom->Dispatch(session, uid);
}

int GameServer::GetRoom(int uid)
{
	auto itr = _uid_roomid_dict.find(uid);
	if (itr == _uid_roomid_dict.end())
	{
		return -1;
	}
	return itr->second;
}

int GameServer::HeartBeat(NetworkSessionBase * session)
{
	JsonPacket packet;
	packet._val = session->_jpacket._val;
	session->Send(packet.Encode());
	return 0;
}

int GameServer::CreateRoom(NetworkSessionBase * session)
{
	int ret;
	Player* player;
	std::tie(ret, player) = AddPlayer(session);
	if (ret == 0)
	{
		return HandleCreateRoom(session,player);
	}
	return 0;
}

int GameServer::JoinInRoom(NetworkSessionBase * session)
{
	int ret;
	Player* player;
	std::tie(ret, player) = AddPlayer(session);
	if (ret == 0)
	{
		return HandleJoinInRoom(session,player);
	}
	return 0;
}

std::tuple<int, Player*> GameServer::AddPlayer(NetworkSessionBase * session)
{
	int ret = -1;
	Player* player(nullptr);
	auto &packet = session->_jpacket;
	int uid = JsonTo<int>(packet["uid"]);
	auto func_new_player = [&]() {
		ret = 0;
		player = new Player();
		if (player->init(uid) == -1)
		{
			GAME_LOG.error("init_player error. [%d]\n", ret);
			ret = -1;
			return;
		}
	};
	//先查用户是否在房间里
	auto itr = _uid_roomid_dict.find(uid);
	//用户不在房间里
	if (itr == _uid_roomid_dict.end())
	{
		func_new_player();
	}
	else//用户在房间里
	{
		int roomid = itr->second;
		auto proom = _rooms[roomid];
		if (proom == nullptr)//用户在的房间不存在
		{
			_uid_roomid_dict.erase(uid);//干掉
			func_new_player();
		}
		else//用户指向的房间存在
		{
			Player* old_player = proom->GetPlayer(uid);
			//如果用户存在这个房间里 要准备好挤人了
			if (old_player)
			{
				int fd = Find<UID_KEY>(uid);
				auto old_session = (*_tcp_network)[fd];
				ret = 1;
				if (nullptr != old_session)//如果套接字存在 踢老用户
				{
					JsonPacket packet;
					packet["cmd"] = SERVER_LOGIN_OTHER_DEVICE;
					packet["uid"] = uid;
					old_session->Send(packet.Encode());
					old_session->Destroy();
				}
				NewLink(session->_fd, uid);
				proom->handler_rebind(old_player);
			}
			else//如果用户不存在这个房间里
			{
				_uid_roomid_dict.erase(uid);//干掉
			}
		}
	}
	return std::make_tuple(ret, player);
}

int GameServer::HandleCreateRoom(NetworkSessionBase * session, Player* player)
{
	//不是开房场
	if (_game_flag > 1000000)
	{
		SendError(session, 54, "本场次不支持开房功能");
		GAME_LOG.error("only kaifang room can open room.\n");
		return -1;
	}
	if (player == nullptr)
	{
		SendError(session, 54, "开房用户非法");
		GAME_LOG.error("HandleCreateRoom, client->player = NULL.\n");
		return -1;
	}
	auto& jpacket = session->_jpacket;
	int player_num = JsonTo<int>(jpacket["player_num"]);		//几个人玩
	int board_num = JsonTo<int>(jpacket["board_num"]);			//玩多少局

	//如果游戏人数不对
	if (player_num < 2 || player_num > 4)
	{
		SendError(session, 54, "游戏人数不对");
		GAME_LOG.error("player_num: %d, must 2 or 3 or 4 \n", player_num);
		return -1;
	}
	//如果用户的状态是已经在座位上了
	if (_uid_roomid_dict.find(player->_uid) != _uid_roomid_dict.end())
	{
		SendError(session, 54, "您已经在座位上了");
		GAME_LOG.error("HandleCreateRoom uid[%d] have been in table\n", player->_uid);
		return -1;
	}
	//查询这样的房间需要多少张房卡
	int need_card = game_sql::query_cost_card_for_room(player_num, board_num);
	if (need_card == -1)
	{
		SendError(session, 54, "开房间类型不对");
		GAME_LOG.error("HandleCreateRoom player_num[%d] board_num[%d] isn't true\n", player_num, board_num);
		return -1;
	}
	//扣除用户的房卡
	if (0 == game_sql::sub_user_room_card(player->_uid, need_card))
	{
		SendError(session, 54, "用户房卡数量不足");
		GAME_LOG.error("HandleCreateRoom uid[%d] without room card[%d]\n", player->_uid, need_card);
		return -1;
	}
	//创建房间
	auto room = _rooms.CreateNewRoom();
	int roomid = room->_roomid;
	if (room->init_private(board_num, player_num) < 0)
	{
		GAME_LOG.error("roomid[%d] init error.\n", roomid);
		SendError(session, 51, "创建房间失败.");
		_rooms.DestoryRoom(roomid);
		return -1;
	}

	//取消定时系统
	room->_has_timer = false;
	room->_priv_uid = player->_uid;
	//如果进入不了房间
	if (room->player_enter_room(player) < 0)
	{
		GAME_LOG.error("creater enter room error.\n");
		_rooms.DestoryRoom(roomid);
		return -1;
	}
	return 0;
}

int GameServer::HandleJoinInRoom(NetworkSessionBase * session, Player* player)
{
	//如果用户的状态是已经在座位上了
	if (_uid_roomid_dict.find(player->_uid) != _uid_roomid_dict.end())
	{
		SendError(session, 54, "您已经在座位上了");
		GAME_LOG.error("HandleJoinInRoom uid[%d] have been in table\n", player->_uid);
		return -1;
	}
	if (player == nullptr)
	{
		SendError(session, 54, "开房用户非法");
		GAME_LOG.error("HandleJoinInRoom, player = nullptr.\n");
		return -1;
	}
	//加入一个收费房间
	if (YXJMJ_SERVER._game_flag < 1000000)
	{
		auto &jpakcet = session->_jpacket;
		int roomid = JsonTo<int>(jpakcet["roomid"]);
		auto room = _rooms[roomid];
		if (room == nullptr)
		{
			GAME_LOG.error("handler_join_room, but room not create.\n", player->_uid);
			SendError(session, 53, "房间号有误");
			return -1;
		}

		if (room->player_enter_room(player) < 0)
		{
			return -1;
		}
		Singleton<RedisClient>::Instance()->
			hset("ROOM_PLAYER_NUM", to_string(room->_rec_id), to_string(room->_players.size()));

	}
	else
		//加入一个普通房间
	{
		auto &jpakcet = session->_jpacket;
		int min_access_gold = 0;
		int max_access_gold = 0;
		//是金币场
		if (YXJMJ_SERVER._game_flag / 1000000 == 1)
		{
			min_access_gold = YXJMJ_SERVER._min_access;
			max_access_gold = YXJMJ_SERVER._max_access;
			if (player->_robot_flag == 0)
			{
				if (player->_gold < min_access_gold)
				{
					GAME_LOG.debug("gold_room handler_join_table, uid[%d] mygold[%d] needgold[%d], gold not enought.\n", player->_uid, player->_gold, min_access_gold);
					SendError(session, 50, "对不起，您的金币不足，请\n使用钻石兑换金币。");
					return -1;
				}
				if (max_access_gold != 0 && player->_gold >= max_access_gold)
				{
					GAME_LOG.debug("gold_room handler_join_table, uid[%d] mygold[%d] max_access_gold[%d], gold too big.\n", player->_uid, player->_gold, max_access_gold);
					SendError(session, 50, "对不起，您的金币过高，请\n前往高级场次参与游戏。");
					return -1;
				}
			}
		}
		else if (YXJMJ_SERVER._game_flag == 2000001)
		{
			min_access_gold = YXJMJ_SERVER._min_access;
			max_access_gold = YXJMJ_SERVER._max_access;
			if (player->_robot_flag == 0)
			{
				if (player->_diamonds < min_access_gold)
				{
					GAME_LOG.debug("gold_room handler_join_table, uid[%d] mydiamonds[%d] needdiamonds[%d], diamonds not enought.\n", player->_uid, player->_diamonds, min_access_gold);
					SendError(session, 50, "对不起，您的钻石不足，请\n去商场充值钻石。");
					return -1;
				}
			}
		}
		//Room* room;
		RoomChooseRule rule;
		if (player->_robot_flag != 1)
		{
			rule._match_id = JsonTo<int>(jpakcet["match_id"]);
		}
		else
		{
			rule._robot_in_wait_sec = 10;
		}
		if (YXJMJ_SERVER._game_flag / 1000000 == 3)
		{
			if (player->_robot_flag == 0 && rule._match_id == 0)
			{
				GAME_LOG.debug("quickly match must input matchid, uid[%d] match_id[%d].\n", player->_uid, rule._match_id);
				SendError(session, 58, "无法定位到此快抢赛");
				return -1;
			}
			if (player->_robot_flag == 0 && game_sql::check_quick_match_count(player->_uid) <= 0)
			{
				GAME_LOG.debug("quickly match not enough, uid[%d]\n", player->_uid);
				SendError(session, 58, "快抢赛资格不足");
				return -1;
			}
		}
		auto room = _rooms.MatchingRoom(&rule);
		if (room == nullptr)
		{
			GAME_LOG.error("no seat\n");
			return -1;
		}
		if (YXJMJ_SERVER._game_flag / 1000000 == 3)
		{
			int match_id = (room->_match_id == 0) ? rule._match_id : room->_match_id;
			int board_count = game_sql::get_shop_match_board_count(match_id);
			if (board_count <= 0)
			{
				SendError(session, 57, "本快抢赛已经结束");
				return -1;
			}
			room->reset_shop_match(board_count);
		}
		if (YXJMJ_SERVER._game_flag / 1000000 == 1)
		{
			auto gm_list = jpakcet["gm_pai_list"];
			if (gm_list.size() == 13)
			{
				stringstream sstr;
				for (int n(0); n < 13; n++)
				{
					int mj_val = JsonTo<int>(gm_list[n]);
					room->_gm_vec.push_back(Mj(mj_val));
					sstr << "<" << mj_val << ">";
				}
				room->_gm = player->_uid;
				GAME_LOG.info("GM enter this game He is %d, His pais are :%s", room->_gm, sstr.str().c_str());
			}
		}
		if (0 == room->player_enter_room(player))
		{
			if (player->_robot_flag == 0)
			{
				room->_match_id = rule._match_id;
			}
			_uid_roomid_dict[player->_uid] = room->_roomid;
		}
		else
		{
			_rooms.LeaveRoom(room->_roomid, room->_players.size());
			return -1;
		}
	}
	return 0;
}

void GameServer::SendError(NetworkSessionBase * session, int error_code, const std::string & err_msg)
{
	JsonPacket packet;
	packet["cmd"] = SERVER_ERROR_UC;
	packet["err"] = error_code;
	packet["err_msg"] = err_msg;
	string str = packet.Encode();
	session->Send(str);
}

bool GameServer::InitGameConfig()
{
	//默认数据库
	if (!_config.isMember("def_database"))
	{
		GAME_LOG.error("can't find default DB name\n");
		return false;
	}
	_default_db = _config["def_database"].asString();
	//是否有数据库连接
	if (!_config.isMember("master"))
	{
		GAME_LOG.error("can't find DB conf\n");
		return false;
	}
	else
	{
		auto db_conf = _config["master"];
		if (db_conf.isMember("mysql_ip") &&
			db_conf.isMember("mysql_port") &&
			db_conf.isMember("mysql_loginname") &&
			db_conf.isMember("mysql_loginpwd")
			)
		{
			if (!MYSQL_CONN->initMysql(db_conf["mysql_ip"].asString(),
				db_conf["mysql_port"].asInt(),
				db_conf["mysql_loginname"].asString(),
				db_conf["mysql_loginpwd"].asString()))
			{
				GAME_LOG.error("init DB fail!\n");
				return false;
			}
			else
			{
				std::cout << "mysql:mysql_ip: " << db_conf["mysql_ip"].asString() << std::endl
					<< "mysql:mysql_port: " << db_conf["mysql_port"].asInt() << std::endl
					<< "mysql:mysql_loginname: " << db_conf["mysql_loginname"].asString() << std::endl
					<< "mysql:mysql_loginpwd: " << db_conf["mysql_loginpwd"].asString() << std::endl
					<< "mysql:def_database: " << _default_db << std::endl;
			}
		}
		else
		{
			GAME_LOG.error("DB conf para isn't full\n");
			return false;
		}
	}
	if (0 != game_sql::get_server_info(_server_id))
	{
		GAME_LOG.error("earn server info fail!\n");
		return false;
	}
	//是否有redis连接
	if (!_config.isMember("redis"))
	{
		GAME_LOG.error("can't find RD conf\n");
		return false;
	}
	else
	{
		auto rd_conf = _config["redis"];
		if (rd_conf.isMember("host") &&
			rd_conf.isMember("port") &&
			rd_conf.isMember("pass") &&
			rd_conf.isMember("timeout")
			)
		{
			if (0 != REDIS_CONN->init(rd_conf["host"].asString(),
				rd_conf["port"].asInt(),
				rd_conf["timeout"].asInt(),
				rd_conf["pass"].asString()))
			{
				GAME_LOG.error("init RD fail!\n");
				return false;
			}
			else
			{
				//redis连接信息
				std::cout << "redis:host: " << rd_conf["host"].asString() << std::endl
					<< "redis:port: " << rd_conf["port"].asInt() << std::endl
					<< "redis:pass: " << rd_conf["pass"].asString() << std::endl;
			}
		}
		else
		{
			GAME_LOG.error("RD conf para isn't full\n");
			return false;
		}
	}
	//是否定义了日志类型
	if (!_config.isMember("log"))
	{
		GAME_LOG.error("can't find log conf\n");
		return false;
	}
	else
	{
		auto log_conf = _config["log"];
		if (log_conf.isMember("log_write_db") &&
			log_conf.isMember("rotate") &&
			log_conf.isMember("max_size") &&
			log_conf.isMember("level") &&
			log_conf.isMember("max_file")
			)
		{
			GAME_LOG.start(_server_id
				, log_conf["level"].asInt()
				, log_conf["log_write_db"].asInt()
				, log_conf["rotate"].asInt()
				, log_conf["max_size"].asInt64()
				, log_conf["max_file"].asInt()
			);
			std::cout << "log:level: " << log_conf["level"].asInt() << std::endl
				<< "log:log_write_db: " << log_conf["log_write_db"].asInt() << std::endl
				<< "log:rotate: " << log_conf["rotate"].asInt() << std::endl
				<< "log:max_size: " << log_conf["max_size"].asInt64() << std::endl
				<< "log:max_file: " << log_conf["max_file"].asInt() << std::endl;
		}
		else
		{
			GAME_LOG.error("log conf para isn't full\n");
			return false;
		}
	}
	//是否定义了游戏类型
	if (!_config.isMember("game"))
	{
		GAME_LOG.error("can't find game conf\n");
		return false;
	}
	else
	{
		auto game_conf = _config["game"];
		if (game_conf.isMember("host") &&
			game_conf.isMember("port") &&
			game_conf.isMember("ws_port") &&
			game_conf.isMember("game_flag") &&
			game_conf.isMember("min_access") &&
			game_conf.isMember("max_access") &&
			game_conf.isMember("base_gold") &&
			game_conf.isMember("tai_fee")
			)
		{
			_ip = game_conf["host"].asString();
			_port = game_conf["port"].asInt();
			_ws_port = game_conf["ws_port"].asInt();
			_game_flag = game_conf["game_flag"].asInt();
			_min_access = game_conf["min_access"].asInt();
			_max_access = game_conf["max_access"].asInt();
			_base_gold = game_conf["base_gold"].asInt();
			_tai_fee = game_conf["tai_fee"].asInt();

			std::cout << "game:host: " << _ip << std::endl
				<< "game:port: " << _port << std::endl
				<< "game:game_flag: " << _game_flag << std::endl
				<< "game:min_access: " << _min_access << std::endl
				<< "game:max_access: " << _max_access << std::endl
				<< "game:base_gold: " << _base_gold << std::endl
				<< "game:tai_fee: " << _tai_fee << std::endl;
		}
		else
		{
			GAME_LOG.error("game conf para isn't full\n");
			return false;
		}
	}
	return true;
}

NetworkSessionBase * GameServer::operator[](int fd)
{
	return (*_tcp_network)[fd];
}

int GameServer::PlayerLeaveRoom(int uid)
{
	int fd = Find<UID_KEY>(uid);
	(*_tcp_network)[fd]->Destroy();
	return _uid_roomid_dict.erase(uid);
}

bool GameServer::Send(int uid, const std::string & str)
{
	auto fd = Find<UID_KEY>(uid);
	if (fd == -1)
	{
		return false;
	}
	auto ptr = (*_tcp_network)[fd];
	if (ptr)
	{
		ptr->Send(str);
	}
	return false;
}
